package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.pojo.UserLike;
import com.tanhua.dubbo.server.pojo.Users;
import com.tanhua.dubbo.server.vo.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Slf4j
@Service(version = "1.0.0")
public class UserLikeApiImpl implements UserLikeApi {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public static final String LIKE_REDIS_KEY_PREFIX = "USER_LIKE_";

    public static final String NOT_LIKE_REDIS_KEY_PREFIX = "USER_NOT_LIKE_";

    /**
     * 喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    @Override
    public Boolean likeUser(Long userId, Long likeUserId) {
        //判断该用户是否已经喜欢，如果已经喜欢就返回false
        if (isLike(userId, likeUserId)) {
            return false;
        }
        UserLike userLike = new UserLike();
        userLike.setId(ObjectId.get());
        userLike.setUserId(userId);
        userLike.setLikeUserId(likeUserId);
        userLike.setCreated(System.currentTimeMillis());
        try {
            //将数据存储到MongoDB
            mongoTemplate.save(userLike);
            //用户的喜欢数据保存到redis
            String redisKey = LIKE_REDIS_KEY_PREFIX + userId;
            String hashKey = StrUtil.toString(likeUserId);
            redisTemplate.opsForHash().put(redisKey, hashKey, "1");
            //判断，喜欢的用户是否在不喜欢的列表中，如果在，就需要删除数据
            if (isUnLike(userId, likeUserId)) {
                redisKey = NOT_LIKE_REDIS_KEY_PREFIX + userId;
                redisTemplate.opsForHash().delete(redisKey, hashKey);
            }
            return true;
        } catch (Exception e) {
            log.error("数据库异常" + e);
        }
        return false;
    }

    /**
     * 不喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    @Override
    public Boolean unLikeUser(Long userId, Long likeUserId) {
        //判断用户是否已经不喜欢，如果已经不喜欢，就返回
        if (isUnLike(userId, likeUserId)) {
            return false;
        }
        try {
            //将不喜欢的记录保存至redis中
            String redisKey = NOT_LIKE_REDIS_KEY_PREFIX + userId;
            String hashKey = StrUtil.toString(likeUserId);
            redisTemplate.opsForHash().put(redisKey, hashKey, "1");
            //判断用户是否在喜欢列表中，如果存在的话，需要删除数据
            if (isLike(userId, likeUserId)) {
                //将redis中的喜欢记录删除
                redisKey = LIKE_REDIS_KEY_PREFIX + userId;
                redisTemplate.opsForHash().delete(redisKey, hashKey);
                //从数据库中删除
                Query query = Query.query(Criteria.where("userId").is(userId).and("likeUserId").is(likeUserId));
                mongoTemplate.remove(query, UserLike.class);
            }
            return true;
        } catch (Exception e) {
            log.error("数据库异常" + e);
        }
        return false;
    }

    /**
     * 是否互相喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    @Override
    public Boolean bothLikeOther(Long userId, Long likeUserId) {
        return isLike(userId, likeUserId) && isLike(likeUserId, userId);
    }

    /**
     * 查询喜欢列表
     * redis中使用哈希结构存储,数据如下
     * 用户1：key -> USER_LIKE_1 , value -> 2, "1"
     *
     * @param userId
     * @return
     */
    @Override
    public List<Long> likeList(Long userId) {
        String redisKey = LIKE_REDIS_KEY_PREFIX + userId;
        Set<Object> keys = redisTemplate.opsForHash().keys(redisKey);
        if (CollUtil.isEmpty(keys)) {
            return ListUtil.empty();
        }
        List<Long> likeList = new ArrayList<>();
        for (Object key : keys) {
            likeList.add(Convert.toLong(key));
        }
        return likeList;
    }

    /**
     * 查询不喜欢列表
     *
     * @param userId
     * @return
     */
    @Override
    public List<Long> unLikeList(Long userId) {
        String redisKey = NOT_LIKE_REDIS_KEY_PREFIX + userId;
        Set<Object> keys = redisTemplate.opsForHash().keys(redisKey);
        if (CollUtil.isEmpty(keys)) {
            return ListUtil.empty();
        }
        List<Long> list = new ArrayList<>();
        for (Object key : keys) {
            list.add(Convert.toLong(key));
        }
        return list;
    }

    /**
     * 是否已经喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    public Boolean isLike(Long userId, Long likeUserId) {
        String redisKey = LIKE_REDIS_KEY_PREFIX + userId;
        String hashKey = StrUtil.toString(likeUserId);
        return redisTemplate.opsForHash().hasKey(redisKey, hashKey);
    }

    /**
     * 是否已经不喜欢
     */
    public Boolean isUnLike(Long userId, Long likeUserId) {
        String redisKey = NOT_LIKE_REDIS_KEY_PREFIX + userId;
        String hashKey = StrUtil.toString(likeUserId);
        return redisTemplate.opsForHash().hasKey(redisKey, hashKey);
    }

    /**
     * 我的粉丝数
     *
     * @param userId
     * @return
     */
    @Override
    public Long likeMeCount(Long userId) {
        //无法通过redis查询完成，必须从Mongodb中查询
        Query query = Query.query(Criteria.where("likeUserId").is(userId));
        return mongoTemplate.count(query, UserLike.class);
    }

    /**
     * 我的喜欢数
     *
     * @param userId
     * @return
     */
    @Override
    public Long myLoveCount(Long userId) {
        String redisKey = LIKE_REDIS_KEY_PREFIX + userId;
        return redisTemplate.opsForHash().size(redisKey);
    }

    /**
     * 互相喜欢数
     * 实现2种方式：第一种：查询redis，第二种：查询MongoDB
     * 建议：优先使用redis查询，其次考虑使用Mongodb
     *
     * @param userId
     * @return
     */
    @Override
    public Long loveEachOtherCount(Long userId) {
        //查询我的喜欢列表,获取到所有我喜欢的用户ID
        List<Long> likeUserIdList = likeList(userId);
        //设计一个记数器
        Long count = 0L;
        for (Long likeUserId : likeUserIdList) {
            String redisKey = LIKE_REDIS_KEY_PREFIX + likeUserId;
            String hashKey = StrUtil.toString(userId);
            //获取对方的喜欢列表中是否有我
            if (redisTemplate.opsForHash().hasKey(redisKey, hashKey)) {
                //有的话计数器+1
                count++;
            }
        }
        return count;
    }

    /**
     * 查找互相喜欢列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<UserLike> findLoveEachOtherList(Long userId, Integer page, Integer pageSize) {
        //查询我的喜欢列表
        List<Long> likeUserIds = likeList(userId);
        //查询喜欢我的人,即用户ID在我的喜欢列表中,对应的喜欢的用户的ID是我
        Query query = Query.query(Criteria.where("userId").in(likeUserIds).and("likeUserId").is(userId));
        return queryList(query, page, pageSize);
    }

    /**
     * 查找我的喜欢列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<UserLike> findMyLikeList(Long userId, Integer page, Integer pageSize) {
        Query query = Query.query(Criteria.where("userId").is(userId));
        return queryList(query, page, pageSize);
    }

    /**
     * 查找我的粉丝列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<UserLike> findMyFansList(Long userId, Integer page, Integer pageSize) {
        Query query = Query.query(Criteria.where("likeUserId").is(userId));
        return queryList(query, page, pageSize);
    }

    /**
     * 公共方法可用于从数据库user_like表中查询数据
     *
     * @param query
     * @param page
     * @param pageSize
     * @return
     */
    private PageInfo<UserLike> queryList(Query query, Integer page, Integer pageSize) {
        Pageable pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("created")));
        query.with(pageRequest);
        List<UserLike> likeList = mongoTemplate.find(query, UserLike.class);
        PageInfo<UserLike> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        pageInfo.setRecords(likeList);
        return pageInfo;
    }
}
